package com.bj58.mis.apm.server.statistics.service.impl;

import com.bj58.mis.apm.server.core.common.result.Page;
import com.bj58.mis.apm.server.core.common.result.SimplePage;
import com.bj58.mis.apm.server.core.util.JsonUtil;
import com.bj58.mis.apm.server.core.util.UnitUtil;
import com.bj58.mis.apm.server.core.util.UnitValue;
import com.bj58.mis.apm.server.statistics.dto.cube.*;
import com.bj58.mis.apm.server.statistics.mapper.CubeMapper;
import com.bj58.mis.apm.server.statistics.query.PageInfo;
import com.bj58.mis.apm.server.statistics.query.cube.CubeQuery;
import com.bj58.mis.apm.server.statistics.service.BaseService;
import com.bj58.mis.apm.server.statistics.service.CubeService;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.constraints.NotNull;
import java.sql.Timestamp;
import java.util.*;

/**
 * @author: zhangfx
 * @create: 2018-06-25 18:40
 **/
@Service
@Transactional
public class CubeServiceImpl extends BaseService<CubeMapper> implements CubeService {


    @Override
    public Page<CubeInfo> queryPage(@NotNull String currUser, @NotNull CubeQuery cubeQuery, @NotNull PageInfo pageInfo) {
        return checkCubeAuth(currUser, cubeQuery.getProjectId())
                .then((isAdmin, authCubeIdList) -> {
                    if (isAdmin && StringUtils.isNotEmpty(cubeQuery.getOwner())) {
                        isAdmin = false;
                        authCubeIdList = this.getAuthCubeIdList(cubeQuery.getOwner(), cubeQuery.getProjectId());
                        if (authCubeIdList == null || authCubeIdList.size() == 0) {
                            return new SimplePage<>(0, Collections.emptyList(), pageInfo.getPage(), pageInfo.getLimit());
                        }
                    }
                    long total = mapper.queryCount(isAdmin, authCubeIdList, cubeQuery);
                    List<CubeInfo> list = Collections.emptyList();
                    if (total > 0) {
                        list = mapper.queryPage(isAdmin, authCubeIdList, cubeQuery, pageInfo);
                        list.forEach(this::manageCubeInfo);
                    }
                    return new SimplePage<>(total, list, pageInfo.getPage(), pageInfo.getLimit());
                })
                .noAuth(() -> new SimplePage<>(0, Collections.emptyList(), pageInfo.getPage(), pageInfo.getLimit()))
                .done();
    }

    @Override
    public CubeInfo get(@NotNull String id) {
        CubeInfo cubeInfo = mapper.get(id);
        manageCubeInfo(cubeInfo);
        return cubeInfo;
    }

    /**
     * 处理CubeInfo一些属性
     *
     * @param cube cube对象
     */
    private void manageCubeInfo(CubeInfo cube) {
        if (cube != null) {
            cube.setOwners(this.getCubeOwners(cube.getId()));
        }
    }


    @Override
    @SuppressWarnings("unchecked")
    public CubeDesign getDesign(@NotNull String cubeId) {
        CubeDesign cubeDesign = new CubeDesign();
        Map<String, Object> designMap = mapper.getDesign(cubeId);
        if (designMap == null) {
            return null;
        }
        cubeDesign.setCubeId((String) designMap.get("cubeId"));
        String dimensionsJson = (String) designMap.get("dimensions");
        if (StringUtils.isNotEmpty(dimensionsJson)) {
            cubeDesign.setDimensions(JsonUtil.fromArrayJson(dimensionsJson, CubeDimensions.class));
        }

        String groupsJson = (String) designMap.get("groups");
        if (StringUtils.isNotEmpty(groupsJson)) {
            cubeDesign.setGroups(new ArrayList<>());

            JsonUtil.fromArrayJson(groupsJson).forEach(groupMap -> {
                CubeGroup cubeGroup = new CubeGroup();
                cubeGroup.setIncludes((List<String>) groupMap.get("includes"));
                Map<String, Object> selectRuleMap = (Map<String, Object>) groupMap.get("select_rule");
                if (selectRuleMap != null) {
                    SelectRule selectRule = new SelectRule();
                    selectRule.setHierarchyDims((List<List<String>>) selectRuleMap.get("hierarchy_dims"));
                    selectRule.setMandatoryDims((List<String>) selectRuleMap.get("mandatory_dims"));
                    selectRule.setJointDims((List<List<String>>) selectRuleMap.get("joint_dims"));
                    cubeGroup.setSelectRule(selectRule);
                }
                cubeDesign.getGroups().add(cubeGroup);
            });
        }



        //构建度量所属列簇的map
        String hbaseMappingJson = (String) designMap.get("hbase_mapping");
        Map<String, String> measureToHbaseMapping = buildMeasureToHbaseMapping(hbaseMappingJson);


        String measuresJson = (String) designMap.get("measures");
        if (StringUtils.isNotEmpty(measuresJson)) {
            cubeDesign.setMeasures(new ArrayList<>());

            JsonUtil.fromArrayJson(measuresJson).forEach(measureMap -> {
                CubeMeasure measure = new CubeMeasure();
                //measure
                measure.setName((String) measureMap.get("name"));
                Map<String, Object> fnMap = (Map<String, Object>) measureMap.get("function");
                if (fnMap != null) {
                    measure.setFnExpression((String) fnMap.get("expression"));
                    measure.setFnReturnType((String) fnMap.get("returntype"));
                    Map<String, Object> pMap = (Map<String, Object>) fnMap.get("parameter");
                    if (pMap != null) {
                        measure.setFnParameterType((String) pMap.get("type"));
                        measure.setFnParameterValue((String) pMap.get("value"));
                    }
                }
                measure.setColumnFamily(measureToHbaseMapping.get(measure.getName()));
                cubeDesign.getMeasures().add(measure);
            });
        }

        //Cube数据分区设置
        //m.partition_desc, m.filter_condition,
        CubePartitionConfig partitionConfig = CubePartitionConfig.fromJson((String) designMap.get("partition_desc"));
        partitionConfig.setWhere((String) designMap.get("filter_condition"));
        cubeDesign.setPartitionConfig(partitionConfig);

        //刷新设置
        //cd.auto_merge_time_ranges, cd.partition_date_start, cd.volatile_range, cd.retention_range
        CubeMergeConfig mergeConfig = new CubeMergeConfig();
        String auto_merge_time_ranges = (String) designMap.get("auto_merge_time_ranges");
        if(auto_merge_time_ranges!=null && auto_merge_time_ranges.length()>0){
            mergeConfig.setAutoMergeTimeRanges(JsonUtil.fromArrayJson(auto_merge_time_ranges, Long.class));
        }
        mergeConfig.setPartitionDateStart((Timestamp) designMap.get("partition_date_start"));
        mergeConfig.setVolatileRange(MapUtils.getLong(designMap, "volatile_range"));
        mergeConfig.setRetentionRange(MapUtils.getLong(designMap, "retention_range"));
        cubeDesign.setMergeConfig(mergeConfig);

        return cubeDesign;
    }

    //构建度量所属列簇的map
    //{"column_family":[{"name":"F1","columns":[{"qualifier":"M","measure_refs":["_COUNT_","SUM_SCORE"]}]}]}
    private Map<String,String> buildMeasureToHbaseMapping(String hbaseMappingJson) {
        Map<String, String> map = new HashMap<>();
        if (StringUtils.isNotEmpty(hbaseMappingJson)) {
            Map<String, Object> hbaseMappingMap = JsonUtil.fromJson(hbaseMappingJson);
            List<Map<String, Object>> columnFamilyList = (List<Map<String, Object>>) hbaseMappingMap.get("column_family");
            if(columnFamilyList!=null){
                columnFamilyList.forEach(columnFamily -> {
                    String columnFamilyName = (String) columnFamily.get("name");
                    List<Map<String, Object>> columnList = (List<Map<String, Object>>) columnFamily.get("columns");
                    columnList.forEach(columnMap -> {
                        List<String> measureRefs = (List<String>) columnMap.get("measure_refs");
                        measureRefs.forEach(measureRef -> {
                            map.put(measureRef, columnFamilyName);
                        });
                    });
                });
            }
        }
        return map;
    }

    @Override
    public List<Segment> getSegments(@NotNull String cubeId) {
        List<Segment> list = mapper.getSegments(cubeId);
        double max = 0;
        for(Segment s : list){
            max = Math.max(max, s.getSize());
        }
        UnitValue uv = UnitUtil.deal(max);
        for(Segment s : list){
            s.setSize(UnitUtil.dealByUnit(s.getSize(), uv.getUint()));
            s.setInputRecordsSize(UnitUtil.dealByUnit(s.getInputRecordsSize(), uv.getUint()));
            s.setUnit(uv.getUint());
        }
        return list;
    }

    @Override
    @SuppressWarnings("unchecked")
    public Model getModel(@NotNull String cubeId) {
        Map<String, Object> modelMap = mapper.getModel(cubeId);
        if (modelMap == null) return null;

        Model model = new Model();
        model.setId((String) modelMap.get("id"));
        model.setTable((String) modelMap.get("fact_table"));


        String dimensionsJson = (String) modelMap.get("dimensions");
        if (StringUtils.isNotEmpty(dimensionsJson)) {
            model.setDimensions(new ArrayList<>());
            for (Map<String, Object> dimensionMap : JsonUtil.fromArrayJson(dimensionsJson)) {
                String table = (String) dimensionMap.get("table");
                List<String> columnList = (List<String>) dimensionMap.get("columns");
                columnList.forEach(column -> model.getDimensions().add(table + "." + column));
            }
        }

        String metricsJson = (String) modelMap.get("metrics");
        if (StringUtils.isNotEmpty(metricsJson)) {
            model.setMetrics(JsonUtil.fromArrayJson(metricsJson, String.class));
        }

        String lookupsJson = (String) modelMap.get("lookups");
        if (StringUtils.isNotEmpty(lookupsJson)) {
            model.setLookups(new ArrayList<>());
            JsonUtil.fromArrayJson(lookupsJson).forEach(lookupMap -> {
                String table = (String) lookupMap.get("table");
                String alias = (String) lookupMap.get("alias");
                Map<String, Object> joinMap = (Map<String, Object>) lookupMap.get("join");
                String joinType = (String) joinMap.get("type");
                List<String> primaryKey = (List<String>)joinMap.get("primary_key");
                List<String> foreignKey = (List<String>)joinMap.get("foreign_key");
                model.getLookups().add(new Lookup(table, alias, joinType, primaryKey, foreignKey));
            });
        }


        return model;
    }


    @Override
    public CubeStatistics queryStatistics(@NotNull String owner, String projectId) {
        return checkCubeAuth(owner, projectId)
                .then((isAdmin, authCubeIdList) -> {
                    CubeStatistics statistics = mapper.queryStatistics(isAdmin, authCubeIdList, projectId);
                    if (isAdmin) {
                        statistics.setRocCube(getTotalRocCube(projectId));
                    } else {
                        statistics.setRocCube(authCubeIdList.size());
                    }
                    return statistics;
                })
                .noAuth(() -> new CubeStatistics())
                .done();
    }


    @Override
    public List<Map<String, Object>> queryTop10LowestUsageColumn(@NotNull String owner, String projectId) {
        return checkCubeAuth(owner, projectId)
                .then((isAdmin, authCubeIdList) -> mapper.queryTop10LowestUsageColumn(isAdmin, authCubeIdList, projectId))
                .noAuth(Collections::emptyList)
                .done();
    }

    @Override
    public List<Map<String, Object>> queryTop10SwellRatio(@NotNull String owner, String projectId) {
        return checkCubeAuth(owner, projectId)
                .then((isAdmin, authCubeIdList) -> mapper.queryTop10SwellRatio(isAdmin, authCubeIdList, projectId))
                .noAuth(Collections::emptyList)
                .done();
    }

    @Override
    public List<Map<String, Object>> queryTop10MostUsedQuery(@NotNull String owner, String projectId, String startDate, String endDate) {
        return checkCubeAuth(owner, projectId)
                .then((isAdmin, authCubeIdList) -> mapper.queryTop10MostUsedQuery(isAdmin, authCubeIdList, projectId, startDate, endDate))
                .noAuth(Collections::emptyList)
                .done();
    }

    @Override
    public List<Map<String, Object>> queryTop10SlowQuery(@NotNull String owner, String projectId, String startDate, String endDate) {
        return checkCubeAuth(owner, projectId)
                .then((isAdmin, authCubeIdList) -> mapper.queryTop10SlowQuery(isAdmin, authCubeIdList, projectId, startDate, endDate))
                .noAuth(Collections::emptyList)
                .done();
    }

    @Override
    public List<Map<String, Object>> queryTop10SlowBuild(@NotNull String owner, String projectId) {
        return checkCubeAuth(owner, projectId)
                .then((isAdmin, authCubeIdList) -> mapper.queryTop10SlowBuild(isAdmin, authCubeIdList, projectId))
                .noAuth(Collections::emptyList)
                .done();
    }

    @Override
    public List<Map<String, Object>> queryTop10DataSize(@NotNull String owner, String projectId) {
        return checkCubeAuth(owner, projectId)
                .then((isAdmin, authCubeIdList) -> mapper.queryTop10DataSize(isAdmin, authCubeIdList, projectId))
                .noAuth(Collections::emptyList)
                .done();
    }

    @Override
    public List<Map<String, Object>> querySwellRangeStatistics(String owner, String projectId) {
        return checkCubeAuth(owner, projectId)
                .then((isAdmin, authCubeIdList) -> mapper.querySwellRangeStatistics(isAdmin, authCubeIdList, projectId))
                .noAuth(Collections::emptyList)
                .done();
    }

}